#include "mut.h"
#include "mutsky.h"

struct SKYVERTEX{
	D3DXVECTOR3 pos;
	D3DXVECTOR2 uv;
};
D3DVERTEXELEMENT9 g_skyelem[] = {
/*	stream, offset, Type,				Method, Usage,					UsageIndex */
	{0,		0,		D3DDECLTYPE_FLOAT3, 0,		D3DDECLUSAGE_POSITION,	0 },
	{0,		12,		D3DDECLTYPE_FLOAT2, 0,		D3DDECLUSAGE_TEXCOORD,	0 },
	D3DDECL_END()
};

const char g_skyEffectData[] = "\
sampler t0 : register(s0);\
float4 PSCloud( float2 vTexCoord : TEXCOORD0 ):COLOR0{\
	float4 retColor = tex2D( t0, vTexCoord );\
	float d = length( vTexCoord - float2( 0.5, 0.5 ) );\
	retColor.a = saturate( 1.0f - retColor.a + d );\
	return retColor;\
}\
technique techCloud{\
	pass p0{\
		PixelShader = compile ps_2_0 PSCloud();\
	}\
}\
";

HRESULT CMUTSky::Create(){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ){
		MUTErrorTrace( L"Invalid device" );
		return E_FAIL;
	}
	HRESULT hr = S_OK;
	if ( FAILED( hr = pDevice->CreateVertexDeclaration( g_skyelem, &vdecl ) ) ){
		MUTDXErrorTrace( hr, L"Sky vdecl failed" );
		return E_FAIL;
	}

	vnum = SKY_ROUNDS*SKY_VNUMPERROUND + 1 + 4/*cloud*/ + 4/*sun*/;
	if ( FAILED( hr = pDevice->CreateVertexBuffer( vnum * sizeof( SKYVERTEX ), 0, 0, D3DPOOL_MANAGED, &vb, NULL ) ) ){
		MUTDXErrorTrace( hr, L"Sky vertex failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = pDevice->CreateIndexBuffer( sizeof( WORD )* ( 2+SKY_VNUMPERROUND + 2*(SKY_ROUNDS-1)*(SKY_VNUMPERROUND+1) + 4/*cloud*/ + 4/*sun*/ ), 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ib, NULL ) ) ){
		MUTDXErrorTrace( hr, L"Sky index failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = pDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pSunQuery ) ) ){
		MUTDXErrorTrace( hr, L"Sun query failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = pDevice->CreateTexture( SKY_TEXWIDTH, SKY_TEXHEIGHT, 0, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &skytex, NULL ) ) ){
		MUTDXErrorTrace( hr, L"Sky tex failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = pDevice->CreateDepthStencilSurface( SKY_TEXWIDTH, SKY_TEXHEIGHT, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &pDSurface, NULL ) ) ){
		MUTDXErrorTrace( hr, L"Sky DSurface failed" );
		return E_FAIL;
	}
	
	if ( FAILED( hr = D3DXCreateTextureFromFile( pDevice, L"sky/cloud.png", &cloud ) ) ){
		MUTDXErrorTrace( hr, L"Cloud tex failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = D3DXCreateTextureFromFile( pDevice, L"sky/domeblend.png", &domeblend ) ) ){
		MUTDXErrorTrace( hr, L"dome blend failed" );
		return E_FAIL;
	}
	
	if ( FAILED( hr = D3DXCreateTextureFromFile( pDevice, L"sky/sun.png", &sun ) ) ){
		MUTDXErrorTrace( hr, L"sun failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = D3DXCreateTextureFromFile( pDevice, L"sky/lensflare.png", &lensflare ) ) ){
		MUTDXErrorTrace( hr, L"lensflare failed" );
		return E_FAIL;
	}

	if ( FAILED( hr = D3DXCreateSprite( pDevice, &sprite ) ) ){
		MUTDXErrorTrace( hr, L"Sky Sprite failed" );
		return E_FAIL;
	}

	LPD3DXBUFFER error = 0;
	if ( FAILED( hr = D3DXCreateEffect( pDevice, g_skyEffectData, sizeof( g_skyEffectData ), NULL, NULL, D3DXSHADER_DEBUG, 0, &skyEffect, &error ) ) ){
		if ( error )
			MUTDXErrorTrace( hr, TString( (char*)error->GetBufferPointer() ) );
		return E_FAIL;
	}

	InitData();
	UpdateSky();
	return S_OK;
}

void CMUTSky::InitData(){
	SKYVERTEX* pVertex = 0;
	vb->Lock( 0, 0, ( void** )&pVertex, 0 );
	int index = 0;
	pVertex[ index ].pos = D3DXVECTOR3( 0, SKY_HEIGHT, 0 );
	pVertex[ index++ ].uv = D3DXVECTOR2( 0.5f, 0.5f );
	
	float fSliceStep = 2.0f * D3DX_PI / SKY_VNUMPERROUND;
	float fStackStep = D3DX_PI / 2.0f / SKY_ROUNDS;
	float fStackTexStep = D3DX_PI / 2.0f / SKY_ROUNDS;
	float r = SKY_HEIGHT;
	float stackRad = 0;
	float sliceRad = 0;
	float fHeight = 0;
	float fTexRadius = 0;
	for ( int i = 0; i < SKY_ROUNDS; i++ ){
		stackRad = ( i + 1 ) * fStackStep;
		fHeight = SKY_HEIGHT * cos( stackRad );
		r = 6.0f * SKY_HEIGHT * sin( stackRad );
		fTexRadius = 0.5f * stackRad / D3DX_PI * 2.0f;
		for ( int j = 0; j < SKY_VNUMPERROUND; j++, index++ ){
			sliceRad = j * fSliceStep;
			pVertex[ index ].pos = D3DXVECTOR3( r * cos( sliceRad ), fHeight, r * sin( sliceRad ) );
			pVertex[ index ].uv = D3DXVECTOR2( 0.5f + fTexRadius * cos( sliceRad ), 0.5f + fTexRadius * sin( sliceRad ) );
			if ( i == SKY_ROUNDS - 1 ){
				pVertex[ index ].pos = D3DXVECTOR3( 0, -10000.0f, 0 );

			}
		}
	}
	
	float cloudRange = 10.0f * SKY_HEIGHT;
	float cloudHeight =  SKY_HEIGHT/2.0f;
	pVertex[ index ].pos = D3DXVECTOR3( -cloudRange, cloudHeight, cloudRange );
	pVertex[ index ].uv = D3DXVECTOR2( 0, 0 );
	pVertex[ index+1 ].pos = D3DXVECTOR3( -cloudRange, cloudHeight, -cloudRange );
	pVertex[ index+1 ].uv = D3DXVECTOR2( 0, 1 );
	pVertex[ index+2 ].pos = D3DXVECTOR3( cloudRange, cloudHeight, cloudRange );
	pVertex[ index+2 ].uv = D3DXVECTOR2( 1, 0 );
	pVertex[ index+3 ].pos = D3DXVECTOR3( cloudRange, cloudHeight, -cloudRange );
	pVertex[ index+3 ].uv = D3DXVECTOR2( 1, 1 );
	index += 4;

	float sunSize = 1000.0f;
	pVertex[ index ].pos = D3DXVECTOR3( -sunSize, sunSize, 0 );
	pVertex[ index ].uv = D3DXVECTOR2( 0, 0 );
	pVertex[ index+1 ].pos = D3DXVECTOR3( sunSize, sunSize, 0 );
	pVertex[ index+1 ].uv = D3DXVECTOR2( 1, 0 );
	pVertex[ index+2 ].pos = D3DXVECTOR3( -sunSize, -sunSize, 0 );
	pVertex[ index+2 ].uv = D3DXVECTOR2( 0, 1 );
	pVertex[ index+3 ].pos = D3DXVECTOR3( sunSize, -sunSize, 0 );
	pVertex[ index+3 ].uv = D3DXVECTOR2( 1, 1 );
	index += 4;

	vb->Unlock();

	WORD* pIndex = 0;
	ib->Lock( 0, 0, ( void** )&pIndex, 0 );

	index = 0;
	// Triangle fan
	for ( int i = 0; i < SKY_VNUMPERROUND+1; i++, index++ )
		pIndex[ index ] = index;
	pIndex[ index++ ] = 1;

	// Triangle Strip
	for ( int i = 0; i < SKY_ROUNDS-1; i++ ){
		for ( int j = 0; j < SKY_VNUMPERROUND; j++ ){
			pIndex[ index++ ] = 1 + i*SKY_VNUMPERROUND + j;
			pIndex[ index++ ] = 1 + (i+1)*SKY_VNUMPERROUND + j;
		}
		pIndex[ index++ ] = 1 + i * SKY_VNUMPERROUND;
		pIndex[ index++ ] = 1 + (i+1) * SKY_VNUMPERROUND;
	}
	//cloud
	pIndex[ index++ ] = vnum-8;
	pIndex[ index++ ] = vnum-7;
	pIndex[ index++ ] = vnum-6;
	pIndex[ index++ ] = vnum-5;
	
	//sun
	pIndex[ index++ ] = vnum-4;
	pIndex[ index++ ] = vnum-3;
	pIndex[ index++ ] = vnum-2;
	pIndex[ index++ ] = vnum-1;

	ib->Unlock();
}

void CMUTSky::UpdateSky(){
	LPDIRECT3DSURFACE9 pSkySurface = 0;
	LPDIRECT3DSURFACE9 pOldSurface = 0;
	LPDIRECT3DSURFACE9 pOldDSurface = 0;
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;
	skytex->GetSurfaceLevel( 0, &pSkySurface );

	D3DVIEWPORT9 oldViewport, curViewport;
	curViewport.Width = SKY_TEXWIDTH;
	curViewport.Height = SKY_TEXHEIGHT;
	curViewport.X = 0;
	curViewport.Y = 0;
	curViewport.MinZ = 0;
	curViewport.MaxZ = 1.0f;

	pDevice->GetRenderTarget( 0, &pOldSurface );
	pDevice->GetDepthStencilSurface( &pOldDSurface );
	pDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
	pDevice->SetRenderTarget( 0, pSkySurface );
	pDevice->SetDepthStencilSurface( pDSurface );
	pDevice->Clear( 0, NULL, D3DCLEAR_TARGET, /*D3DCOLOR_XRGB( 255, 190, 0 )*/D3DCOLOR_XRGB( 255, 255, 255 ), 0, 0 );
	pDevice->BeginScene();

	sprite->Begin( D3DXSPRITE_ALPHABLEND );
	
	//pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	//pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR );
	/*
	for ( int i = 0; i < 100 ; i++ ){
		D3DXMATRIX matScale, matTrans;
		int x = rand()%1000, y = rand()%1000;
		float d = sqrt(float(x - 512)*(x - 512)+(y - 512)*(y - 512))/512.0f;
		d = cos( d*D3DX_PI/2 );
		D3DXMatrixScaling( &matScale, d, d, 0 );
		D3DXMatrixTranslation( &matTrans, x, y, 0 );
		sprite->SetTransform( &( matScale * matTrans ) );
		sprite->Draw( cloud, NULL, NULL, NULL , D3DCOLOR_XRGB( 255, 255, 255 ) );
	}*/
	//pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	//sprite->Draw( domeblend, NULL, NULL, NULL , D3DCOLOR_XRGB( 255,255,255 ) );
	sprite->Draw( domeblend, NULL, NULL, NULL , /*D3DCOLOR_XRGB( 170,200,255 )*/D3DCOLOR_XRGB( 60, 120, 240 ) );
	//sprite->Draw( sun, NULL, NULL, &D3DXVECTOR3(200,200,0) , D3DCOLOR_XRGB( 255, 255, 255 ) );
	
	sprite->End();

	pDevice->EndScene();
	pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
	pDevice->SetRenderTarget( 0, pOldSurface );
	pDevice->SetDepthStencilSurface( pOldDSurface );

	pOldSurface->Release();
	pSkySurface->Release();
	pOldDSurface->Release();
}

void CMUTSky::Render(){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;
	D3DXMATRIX matIdentity, matView, matNoTransView;
	D3DXMatrixIdentity( &matIdentity );
	pDevice->SetTransform( D3DTS_WORLD, &matIdentity );
	pDevice->GetTransform( D3DTS_VIEW, &matView );
	matNoTransView = matView; 
	matNoTransView._41 = matNoTransView._42 = matNoTransView._43 = 0;
	pDevice->SetTransform( D3DTS_VIEW, &matNoTransView );
	pDevice->SetRenderState( D3DRS_ZENABLE, FALSE );

	pDevice->SetTexture( 0, skytex );
	pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	pDevice->SetVertexDeclaration( vdecl );
	pDevice->SetStreamSource( 0, vb, 0, sizeof( SKYVERTEX ) );
	pDevice->SetIndices( ib );
	pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLEFAN, 0, 0, vnum, 0, SKY_VNUMPERROUND );
	for ( int i = 0; i < SKY_ROUNDS - 1; i++ )
		pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, vnum, SKY_VNUMPERROUND+2 + i*2*(SKY_VNUMPERROUND+1), 2*SKY_VNUMPERROUND );
	
	pDevice->SetTransform( D3DTS_VIEW, &matView );
	pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
}

void CMUTSky::RenderSun( BOOL ZEnable ){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;

	D3DXMATRIX matView, matNoTransView, matWorld, matBillboard;
	pDevice->GetTransform( D3DTS_VIEW, &matView );
	matNoTransView = matView; 
	matNoTransView._41 = matNoTransView._42 = matNoTransView._43 = 0;
	pDevice->SetRenderState( D3DRS_ZENABLE, ZEnable );
	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
	pDevice->SetTransform( D3DTS_VIEW, &matNoTransView );
	
	D3DXMatrixTranspose( &matBillboard, &matNoTransView );

	pDevice->SetTexture( 0, sun );

	pDevice->SetVertexDeclaration( vdecl );
	pDevice->SetStreamSource( 0, vb, 0, sizeof( SKYVERTEX ) );
	pDevice->SetIndices( ib );

	int indexBias = SKY_VNUMPERROUND + 2 + 2*(SKY_ROUNDS-1)*(SKY_VNUMPERROUND+1);
	float sunDis = 10000.0f;
	float sunHeight = SKY_HEIGHT;
	D3DXMatrixTranslation( &matWorld, 0, sunHeight, sunDis );
	D3DXMatrixMultiply( &matWorld, &matBillboard, &matWorld );
	pDevice->SetTransform( D3DTS_WORLD, &matWorld );
	pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, vnum-5, 4, indexBias + 4, 2 );
	

	pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	pDevice->SetTransform( D3DTS_VIEW, &matView );
}

void CMUTSky::RenderCloud(){
	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	if ( !pDevice ) return;

	D3DXMATRIX matView, matNoTransView, matWorld;
	pDevice->GetTransform( D3DTS_VIEW, &matView );
	matNoTransView = matView; 
	matNoTransView._41 = matNoTransView._42 = matNoTransView._43 = 0;
	pDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );

	pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA );
	pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_SRCALPHA );
	pDevice->SetTransform( D3DTS_VIEW, &matNoTransView );

	UINT Passes = 0;
	skyEffect->Begin( &Passes, 0 );
	skyEffect->BeginPass( 0 );
	pDevice->SetVertexDeclaration( vdecl );
	pDevice->SetStreamSource( 0, vb, 0, sizeof( SKYVERTEX ) );
	pDevice->SetIndices( ib );

	int indexBias = SKY_VNUMPERROUND + 2 + 2*(SKY_ROUNDS-1)*(SKY_VNUMPERROUND+1);

	pDevice->SetTexture( 0, cloud );
	D3DXMatrixIdentity( &matWorld );
	pDevice->SetTransform( D3DTS_WORLD, &matWorld );
	pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, vnum-9, 4, indexBias, 2 );
	
	skyEffect->EndPass();
	skyEffect->End();
	pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	pDevice->SetTransform( D3DTS_VIEW, &matView );
}

void CMUTSky::RenderLensFlare(){
	DWORD numPixelsDrawn = 0;
	while ( S_FALSE == pSunQuery->GetData( &numPixelsDrawn, sizeof( DWORD ), D3DGETDATA_FLUSH ) );
	if ( numPixelsDrawn ){
		float light = numPixelsDrawn / 17000.0f;
		D3DXVECTOR4 sunPos( 0, SKY_HEIGHT, 10000.0f, 1.0f );
		LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
		pDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
		D3DXMATRIX matView, matProj;
		pDevice->GetTransform( D3DTS_VIEW, &matView );
		pDevice->GetTransform( D3DTS_PROJECTION, &matProj );
		matView._41 = matView._42 =  matView._43 = 0;
		D3DXVec4Transform( &sunPos, &sunPos, &matView );
		D3DXVec4Transform( &sunPos, &sunPos, &matProj );
		sunPos /= sunPos.w;
		float width = MUTState()->GetD3DPP_BackBufferWidth();
		float height = MUTState()->GetD3DPP_BackBufferHeight();
		sunPos.x = ( sunPos.x + 1.0f ) / 2.0f * width;
		sunPos.y = ( 1.0f - sunPos.y ) / 2.0f * height;

		D3DXVECTOR2 center( width/2.0f, height/2.0f );
		D3DXVECTOR2 center2sun( D3DXVECTOR2(sunPos) - center );
		float length = D3DXVec2Length( &center2sun );
		D3DXVec2Normalize( &center2sun, &center2sun );

		float lengthScale[7] = { length,	length/2,	length/3,	length/8,	-length/2, -length/4,	-length/5.5 };
		float imageScale[7]	 = { 1.0,		0.5,		0.25,		1.0,		0.5,		0.25,		0.25 };

		sprite->Begin( D3DXSPRITE_ALPHABLEND );
		D3DXMATRIX matTrans, matScaling;
		for ( int i = 0; i < 7; i++ ){
			D3DXMatrixTranslation( &matTrans, center.x + center2sun.x * lengthScale[i], center.y + center2sun.y * lengthScale[i], 0 );
			D3DXMatrixScaling( &matScaling, imageScale[i], imageScale[i], 1.0f );
			sprite->SetTransform( &( matScaling * matTrans ) );
			sprite->Draw( lensflare, NULL, &D3DXVECTOR3( 128, 128, 0 ), NULL, D3DCOLOR_ARGB( int( ( 255 - 40*i )*light ),250,250,250 ) );
		}
		sprite->End();
		pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
	}

	QuerySun();
}

void CMUTSky::QuerySun(){
	pSunQuery->Issue( D3DISSUE_BEGIN );

	LPDIRECT3DDEVICE9 pDevice = MUTState()->GetDevice();
	pDevice->SetRenderState( D3DRS_COLORWRITEENABLE, 0 );
	pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
	RenderSun( TRUE );
	pDevice->SetRenderState( D3DRS_COLORWRITEENABLE, 0xf );
	pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );

	pSunQuery->Issue( D3DISSUE_END );
}

void CMUTSky::Release(){
	if ( vdecl )
		vdecl->Release();
	if ( vb )
		vb->Release();
	if ( ib )
		ib->Release();
	if ( skytex )
		skytex->Release();
	if ( cloud )
		cloud->Release();
	if ( sun )
		sun->Release();
	if ( domeblend )
		domeblend->Release();
	if ( lensflare )
		lensflare->Release();
	if ( pDSurface )
		pDSurface->Release();
	if ( pSunQuery )
		pSunQuery->Release();
	if ( sprite )
		sprite->Release();
	if ( skyEffect )
		skyEffect->Release();
	vdecl = 0;
	vb = 0;
	ib = 0;
	skytex = 0;
	cloud = 0;
	sun = 0;
	domeblend = 0;
	lensflare = 0;
	pDSurface = 0;
	pSunQuery = 0;
	sprite = 0;
	skyEffect = 0;
}
/*
CMUTSky sky;

void MUTSkyCreateTest(){
	sky.Create();
}

void MUTSkyRenderTest(){
	sky.Render();
}
void MUTSkyRenderTestPreMap(){
	sky.RenderCloud();
}*/